﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Mitosoft.Definitions.Range;

namespace Mitosoft.Definitions.Data
{
  /// <summary>
  /// Provides cached blocked data based on a collection of data providers delivering data in blocks.
  /// </summary>
  public class CachedCollectionBlockDataProvider<D, V> : IList, INotifyCollectionChanged, ICacheableDataBlockProvider<D, V> where D : IGenericDataBlockItem<V>
  {
    #region delegates and events

    /// <summary>
    /// Occurs when a new instance is requested.
    /// </summary>
    public event CreateDataBlockItemInstanceDelegate DataBlockItemInstanceRequest;

    #endregion

    #region fields

    /// <summary>
		/// Represents <see cref="DataBlockProviders"/>
		/// The key of the dictionary represents the order of the data block provider.
		/// </summary>
		protected Dictionary<int, ICacheableDataBlockProvider<D, V>> _dataBlockProviders;

    /// <summary>
    /// Contains ranges resulting from the data block providers.
    /// The key of the dictionary represents the order of the data block.
    /// </summary>
    protected Dictionary<int, KeyValuePair<ulong, Range<ulong>>> _dataBlockProviderRanges = 
      new Dictionary<int, KeyValuePair<ulong, Range<ulong>>>();

		/// <summary>
		/// Represents <see cref="CacheSize"/>
		/// </summary>
		protected ulong _cacheSize = 1024;

		/// <summary>
		/// Represents <see cref="DataItemsCacheReference"/>
		/// </summary>
		protected IEnumerable<D> _dataItemsCacheReference;

		/// <summary>
		/// Collection containing the cached data items.
		/// </summary>
		protected readonly List<D> _cachedDataItems = new List<D>();

		/// <summary>
		/// Used to lock <see><cref>this[int]</cref></see> and - or <see cref="Values"/>
		/// </summary>
		protected readonly object _indexAndValuesLock = new object();

		/// <summary>
		/// Represents <see cref="BlockIListRequests"/>
		/// </summary>
		protected bool _blockIListRequests;

		/// <summary>
		/// Represents <see cref="ListRequestWasRejected"/>
		/// </summary>
		protected bool _iListRequestWasRejected;

		public event NotifyCollectionChangedEventHandler CollectionChanged;

		/// <summary>
		/// Context used to throw <see cref="CollectionChanged"/>
		/// </summary>
		public readonly SynchronizationContext _synchronizationContext;

    protected ulong _totalNumberOfRecords;

		#endregion

		#region constructors

		/// <summary>
		/// Creates an <see cref="CachedBlockDataProvider{D,V}"/> instance.
		/// </summary>
		/// <param name="dataBlockProviders">Represents the data block providers.</param>
		public CachedCollectionBlockDataProvider( 
      Dictionary<int, ICacheableDataBlockProvider<D, V>> dataBlockProviders )
		{
			if( dataBlockProviders == null ) throw new ArgumentNullException( "dataBlockProviders" );
			DataBlockProviders = dataBlockProviders;
		}

		/// <summary>
		/// Creates an <see cref="CachedBlockDataProvider{D,V}"/> instance.
		/// </summary>
		/// <param name="dataBlockProviders">Represents the data block provider.</param>
		/// <param name="cacheSize">Represents <see cref="CacheSize"/></param>
		/// <param name="synchronizationContext">Context used to throw <see cref="CollectionChanged"/> in the UI thread.</param>
    public CachedCollectionBlockDataProvider( 
			Dictionary<int, ICacheableDataBlockProvider<D, V>> dataBlockProviders, 
			ulong cacheSize, 
			SynchronizationContext synchronizationContext )
			: this( dataBlockProviders )
		{
			_synchronizationContext = synchronizationContext;
			CacheSize = cacheSize;
		}

		#endregion

		#region properties and methods

    /// <summary>
    /// Calculates and saves the range values being defined by the data block providers.
    /// </summary>
    protected void ActualizeDataBlockProviderRanges()
    {
      _dataBlockProviderRanges.Clear();
      ulong offset = 0;
      ulong sumOftotalNumberOfRecords = 0;

      foreach (KeyValuePair<int, ICacheableDataBlockProvider<D, V>> cacheableDataBlockProvider in DataBlockProviders)
      {
        ulong totalNumberOfRecords = cacheableDataBlockProvider.Value.TotalNumberOfRecords;
        sumOftotalNumberOfRecords += totalNumberOfRecords;

        _dataBlockProviderRanges.Add(
          cacheableDataBlockProvider.Key,
          new KeyValuePair<ulong, Range<ulong>>(
            offset,
            totalNumberOfRecords <= 0 ? 
              null :
              new Range<ulong>(
                offset,
                offset + totalNumberOfRecords - 1)));

        offset += totalNumberOfRecords;
      }

      TotalNumberOfRecords = sumOftotalNumberOfRecords;
    }

    /// <summary>
    /// Gets the offset of the data provider being identified by the index.
    /// </summary>
    /// <param name="indexOfDataProvider">Identifies the data provider.</param>
    /// <returns>Found offset or 0 if no offset was found.</returns>
    public ulong GetDataProviderOffset(int indexOfDataProvider)
    {
      return 
        !_dataBlockProviderRanges.ContainsKey(indexOfDataProvider) ? 
          0 : 
          _dataBlockProviderRanges[indexOfDataProvider].Key;
    }

    /// <summary>
		/// Gets or sets whether IList requests should be blocked.
		/// </summary>
		public bool BlockIListRequests
		{
			get
			{
				return _blockIListRequests;
			}
			set
			{
				if (value == _blockIListRequests) 
					return;	// Exit in case the value does not change

				if (ListRequestWasRejected						// action only needed if a request was rejected
				    && _blockIListRequests && !value			// interested in change from true to false
				   )
				{
					ListRequestWasRejected = false; // reset

					NotifyCollectionChangedEventHandler h = CollectionChanged;
					if (h != null && _synchronizationContext != null)
						_synchronizationContext.Send(
							x => h(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)), null);
				}
					
				_blockIListRequests = value;
			}
		}

		/// <summary>
		/// Gets or sets whether an IList request has been rejected.
		/// </summary>
		protected bool ListRequestWasRejected
		{
			get
			{
				return _iListRequestWasRejected;
			}
			set
			{
				_iListRequestWasRejected = value;
			}
		}

		/// <summary>
		/// Gets or sets the cache size.
		/// Default value is 1024.
		/// </summary>
		protected ulong CacheSize
		{
			get
			{
				return _cacheSize;
			}
			set
			{
				_cacheSize = value;
			}
		}

		/// <summary>
		/// Represents the data block provider used to get data we want to cache.
		/// </summary>
		protected Dictionary<int, ICacheableDataBlockProvider<D, V>> DataBlockProviders
		{
			get
			{
				return _dataBlockProviders;
			}
		  set
		  {
		    _dataBlockProviders = value;
        ActualizeDataBlockProviderRanges();
		  }
		}

		/// <summary>
		/// Collection containing data items representing cache.
		/// </summary>
		protected IEnumerable<D> DataItemsCacheReference
		{
			get
			{
        return _dataItemsCacheReference ?? (_dataItemsCacheReference = _cachedDataItems);
			}
			set
			{
        _dataItemsCacheReference = value;
			}
		}

		/// <summary>
		/// Represents the index of the first data item in the content cache. 
		/// If the content cache is not defined or it is empty, 0 will be returned.
		/// </summary>
		protected ulong? IndexOfFirstDataItemInCache
		{
			get
			{
				if( DataItemsCacheReference == null || !DataItemsCacheReference.Any() )
					return null;

				return DataItemsCacheReference.First().Id;
			}
		}

		/// <summary>
		/// Represents the index of the last data item in the content cache. 
		/// If the content cache is not defined or it is empty, null will be returned.
		/// </summary>
		protected ulong? IndexOfLastDataItemInCache
		{
			get
			{
				if( DataItemsCacheReference == null || !DataItemsCacheReference.Any() )
					return null;

				return DataItemsCacheReference.Last().Id;
			}
		}

		/// <summary>
		/// Retrieves the index of the line content in the cache.
		/// </summary>
		/// <param name="index">Identifies the data item.</param>
		/// <returns>Found index or null if nothing was found</returns>
		protected ulong? IndexOfDataItemInsideCache( ulong index )
		{
			// Return null if cache is undefined or empty:
			if( DataItemsCacheReference == null || !DataItemsCacheReference.Any() )
				return null;

			ulong indexCounter = 0;

			foreach( var d in DataItemsCacheReference )
			{
				if( d != null && d.Id == index )
					return indexCounter;

				indexCounter++;
			}

			return null;  // Nothing found
		}

		/// <summary>
		/// Retrieves the data items from the cache. 
		/// If the required range is completely available in the cache, 
		/// the required line contents will be returned. 
		/// If the required range is not completely covered by the cache, 
		/// null will be returned.
		/// </summary>
		/// <param name="indexOfStartingDataItem">Index of the data item we want start reading the contents.</param>
		/// <param name="numberOfRequestedDataItems">Represents the number of requested data items,
		/// including the data item identified by indexOfStartingDataItem</param>
		/// <returns>Retrieved data items or null if the required range is not completely
		/// covered by the cache.</returns>
		protected IEnumerable<D> GetContentsFromCache(
		  ulong? indexOfStartingDataItem, ulong numberOfRequestedDataItems )
		{
		  lock (_indexAndValuesLock)
		  {
		    if (numberOfRequestedDataItems == 0)
		      yield break;

		    ulong correctedStartingLineNumber =
		      indexOfStartingDataItem != null
		        ? (ulong) indexOfStartingDataItem
		        : (IndexOfFirstDataItemInCache == null ? 0 : (ulong) IndexOfFirstDataItemInCache);

		    ulong? indexOfFirstDataItemInCache = IndexOfFirstDataItemInCache;
		    ulong? indexOfLastDataItemInCache = IndexOfLastDataItemInCache;

		    if (indexOfFirstDataItemInCache == null || indexOfLastDataItemInCache == null)
		      yield break;

		    // Return null if the requested range is not contained completely in the cache:
		    if (correctedStartingLineNumber < indexOfFirstDataItemInCache ||
		        correctedStartingLineNumber > indexOfLastDataItemInCache ||
		        correctedStartingLineNumber + numberOfRequestedDataItems - 1 < indexOfFirstDataItemInCache ||
		        correctedStartingLineNumber + numberOfRequestedDataItems - 1 > indexOfLastDataItemInCache)
		      yield break;

		    // Get now the line contents from the line content cache:
		    ulong? startingIndexInCache = IndexOfDataItemInsideCache(correctedStartingLineNumber);

		    if (startingIndexInCache == null)
		      throw new ApplicationException(
		        string.Format(
		          "{0} {1} {2} {3}",
		          "Unlogical state in method GetContentsFromCache(). The starting index of the line number",
		          correctedStartingLineNumber, "could not be found although the requested range is completely",
		          "covered by the cache!?"));

		    for (uint i = 0; i < numberOfRequestedDataItems; i++)
		    {
		      yield return DataItemsCacheReference.ElementAt((int) (startingIndexInCache + i));
		    }
		  }
		}

		/// <summary>
		/// Retrieves data records.
		/// </summary>
		/// <param name="id">ID of the record we want to start from.</param>
		/// <param name="maximumNumberOfRecords">Maximum number of records we want to retrieve.</param>
		/// <returns>Collection of <see cref="IGenericDataBlockItem{V}"/> values.</returns>
		public IEnumerable<D> Values( ulong id, ulong maximumNumberOfRecords )
		{
		  lock( _indexAndValuesLock )
		  {
		    // if the number of the required lines is greater then the number of lines 
		    // which fits in the cache, we retrieve contents from the data provider:
		    if (maximumNumberOfRecords > CacheSize)
		      return
            DataBlockProvidersValues(id, maximumNumberOfRecords);

		    // Get contents from cache:
		    IEnumerable<D> cachedValues = GetContentsFromCache(id, maximumNumberOfRecords);

		    // Return cached content if available:
			  IEnumerable<D> enumerable = cachedValues as IList<D> ?? cachedValues.ToList();
			  if (enumerable.Any())
				  return enumerable;

		    // Correct the starting index, so it will be in the middle of the cache:
		    ulong startingIndexToFillTheCache = CacheStartingIndex(id);

		    // Fill the cache with data requested from the provider:
        IEnumerable<D> dataItemsCache = DataBlockProvidersValues(startingIndexToFillTheCache, CacheSize);

		    // Save data in cache:
		    _cachedDataItems.Clear();

        // Get starting offset:
		    ulong offsetOrCurrentIndex = 
          (from dataBlockProviderRange in _dataBlockProviderRanges 
           where 
            dataBlockProviderRange.Value.Value != null 
            && dataBlockProviderRange.Value.Value.OverlappingRange(
                  new Range<ulong>(startingIndexToFillTheCache, startingIndexToFillTheCache)) != null 
           select dataBlockProviderRange.Value.Key).First();

		    bool firstTime = true;
		    foreach (var d in dataItemsCache)
        {
          if (firstTime)
          {
            d.Id += offsetOrCurrentIndex;
            offsetOrCurrentIndex = d.Id + 1;
            firstTime = false;
          }
          else
            d.Id = offsetOrCurrentIndex++;

          _cachedDataItems.Add(d);
		    }

		    // Actualize reference:
		    DataItemsCacheReference = _cachedDataItems;

		    // Cache should be now actualized, therefore we return the cached values:
		    return GetContentsFromCache(id, maximumNumberOfRecords);
		  }
		}

    /// <summary>
    /// Gets the required data from the data block providers.
    /// </summary>
    /// <param name="startingIndex">Index of the data we want to start having the values.</param>
    /// <param name="numberOfRecords">Number of required records.</param>
    /// <returns>Retrieved data or null if no data is available.</returns>
    protected IEnumerable<D> DataBlockProvidersValues(ulong startingIndex, ulong numberOfRecords)
    {
      IEnumerable<D> result = null;
      var range = new Range<ulong>(startingIndex, startingIndex + numberOfRecords - 1);

      foreach (KeyValuePair<int, KeyValuePair<ulong, Range<ulong>>> dataBlockProviderRange in _dataBlockProviderRanges)
      {
        if (dataBlockProviderRange.Value.Value == null) continue;
        Range<ulong> overlappingRange = dataBlockProviderRange.Value.Value.OverlappingRange(range);
        if (overlappingRange == null) continue;

        IEnumerable<D> currentProviderData =
          DataBlockProviders[dataBlockProviderRange.Key].Values(
            overlappingRange.Start - dataBlockProviderRange.Value.Key,  // Start - offset!
            overlappingRange.End - overlappingRange.Start + 1);

        result = result == null ? currentProviderData : result.Concat(currentProviderData);
      }

      return result;
    }

		/// <summary>
		/// Represents the number of lines of all files.
		/// </summary>
		public ulong TotalNumberOfRecords
		{
			get
			{
			  return _totalNumberOfRecords;
			}
		  set
		  {
		    _totalNumberOfRecords = value;
		  }
		}

		/// <summary>
		/// Returns the first index to be read in the cache.
		/// Ideally the given current index will be in the middle of the cache lines. 
		/// Special cases are the beginning and the end of the file.
		/// </summary>
		/// <param name="currentIndex">Current data item index.</param>
		/// <returns>Calculated number of the first index in the cache.</returns>
		protected ulong CacheStartingIndex( ulong currentIndex )
		{
			var resultingStartingLineNumber =
			  (ulong)Math.Max( 0, (long)currentIndex - (long)CacheSize / 2 );

      if (resultingStartingLineNumber + CacheSize > TotalNumberOfRecords)
				resultingStartingLineNumber =
          (ulong)(Math.Max(0, (long)TotalNumberOfRecords - (long)CacheSize));

			return resultingStartingLineNumber;
		}

		#endregion

		#region IList

		/// <summary>
		/// Returns an enumerator that iterates through a collection.
		/// </summary>
		/// <returns>
		/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
		/// </returns>
		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		public IEnumerator<D> GetEnumerator()
		{
			return DataItemsCacheReference.GetEnumerator();
		}

		/// <summary>
		/// Gets the number of elements contained in the <see cref="T:System.Collections.ICollection"/>.
		/// </summary>
		/// <returns>
		/// The number of elements contained in the <see cref="T:System.Collections.ICollection"/>.
		/// </returns>
		/// <filterpriority>2</filterpriority>
		public int Count
		{
		  get
		  {
		    return (int)TotalNumberOfRecords;
		  }
		  private set
			{
				throw new NotImplementedException();
			}
		}

		/// <summary>
		/// Determines the index of a specific item in the <see cref="T:System.Collections.IList"/>.
		/// </summary>
		/// <returns>
		/// The index of <paramref name="value"/> if found in the list; otherwise, -1.
		/// </returns>
		/// <param name="value">The object to locate in the <see cref="T:System.Collections.IList"/>. </param><filterpriority>2</filterpriority>
		public int IndexOf( object value )
		{
			lock (_indexAndValuesLock)
			{
				if (BlockIListRequests)
				{
					ListRequestWasRejected = true;
					return -1;
				}

				if (!(value is D)) 
          throw new ArgumentException(
            string.Format(
              "{0}", 
              "Wrong type detected in calculating IndexOf()"));

        var item = this[(int)((D)value).Id];
        if (!(item is D)) return -1;

			  return (int) ((D) item).Id;
			}
		}

		/// <summary>
		/// Gets or sets the element at the specified index.
		/// </summary>
		/// <returns>
		/// The element at the specified index.
		/// </returns>
		/// <param name="index">The zero-based index of the element to get or set. </param><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> is not a valid index in the <see cref="T:System.Collections.IList"/>. </exception><exception cref="T:System.NotSupportedException">The property is set and the <see cref="T:System.Collections.IList"/> is read-only. </exception><filterpriority>2</filterpriority>
		public object this[int index]
		{
			get
			{
				lock (_indexAndValuesLock)
				{
					if( BlockIListRequests )
					{
						ListRequestWasRejected = true;
						return default( D );
					}

					//Debug.Print(string.Format("{0} Index[{1}]", System.DateTime.Now.ToString("HH:MM:ss.fff"), index ));

					IEnumerable<D> values = Values((ulong)index, 1);

					IEnumerable<D> enumerable = values as IList<D> ?? values.ToList();
				  
          if (!enumerable.Any())
            return default(D);
				  
          var handler = DataBlockItemInstanceRequest;
				  object element = enumerable.ElementAt(0);

				  if (handler != null && element is IGenericDataBlockItem<string>)
				  {
				    int providerOrder = 
              (from currentOrderAndRangeDefinitionKVP 
               in   _dataBlockProviderRanges 
               where 
                currentOrderAndRangeDefinitionKVP.Value.Value != null 
                && currentOrderAndRangeDefinitionKVP.Value.Value.OverlappingRange(new Range<ulong>((ulong) index, (ulong) index)) != null 
               select currentOrderAndRangeDefinitionKVP.Key).First();

				    return handler(
              (IGenericDataBlockItem<string>) element,
              providerOrder );
          }
            
				  return element;
				}
			}

			set
			{
				throw new NotImplementedException();
			}
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="T:System.Collections.IList"/> is read-only.
		/// </summary>
		/// <returns>
		/// true if the <see cref="T:System.Collections.IList"/> is read-only; otherwise, false.
		/// </returns>
		public bool IsReadOnly
		{
			get
			{
				return true;
			}

			private set
			{
				throw new NotImplementedException("IsReadOnly");
			}
		}

		/// <summary>
		/// Removes the <see cref="T:System.Collections.IList"/> item at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index of the item to remove. </param><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> is not a valid index in the <see cref="T:System.Collections.IList"/>. </exception><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IList"/> is read-only.-or- The <see cref="T:System.Collections.IList"/> has a fixed size. </exception><filterpriority>2</filterpriority>
		public void RemoveAt( int index )
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Removes all items from the <see cref="T:System.Collections.IList"/>.
		/// </summary>
		/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IList"/> is read-only. </exception><filterpriority>2</filterpriority>
		public void Clear()
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Copies the elements of the <see cref="T:System.Collections.ICollection"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
		/// </summary>
		/// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.ICollection"/>. The <see cref="T:System.Array"/> must have zero-based indexing. </param><param name="index">The zero-based index in <paramref name="array"/> at which copying begins. </param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> is null. </exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> is less than zero. </exception><exception cref="T:System.ArgumentException"><paramref name="array"/> is multidimensional.-or- The number of elements in the source <see cref="T:System.Collections.ICollection"/> is greater than the available space from <paramref name="index"/> to the end of the destination <paramref name="array"/>.-or-The type of the source <see cref="T:System.Collections.ICollection"/> cannot be cast automatically to the type of the destination <paramref name="array"/>.</exception><filterpriority>2</filterpriority>
		public void CopyTo( Array array, int index )
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Gets an object that can be used to synchronize access to the <see cref="T:System.Collections.ICollection"/>.
		/// </summary>
		/// <returns>
		/// An object that can be used to synchronize access to the <see cref="T:System.Collections.ICollection"/>.
		/// </returns>
		public object SyncRoot
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets a value indicating whether access to the <see cref="T:System.Collections.ICollection"/> is synchronized (thread safe).
		/// </summary>
		/// <returns>
		/// true if access to the <see cref="T:System.Collections.ICollection"/> is synchronized (thread safe); otherwise, false.
		/// </returns>
		public bool IsSynchronized
		{
			get;
			private set;
		}

		/// <summary>
		/// Adds an item to the <see cref="T:System.Collections.IList"/>.
		/// </summary>
		/// <returns>
		/// The position into which the new element was inserted, or -1 to indicate that the item was not inserted into the collection,
		/// </returns>
		/// <param name="value">The object to add to the <see cref="T:System.Collections.IList"/>. </param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IList"/> is read-only.-or- The <see cref="T:System.Collections.IList"/> has a fixed size. </exception><filterpriority>2</filterpriority>
		public int Add( object value )
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Determines whether the <see cref="T:System.Collections.IList"/> contains a specific value.
		/// </summary>
		/// <returns>
		/// true if the <see cref="T:System.Object"/> is found in the <see cref="T:System.Collections.IList"/>; otherwise, false.
		/// </returns>
		public bool Contains( object value )
		{
			if (!(value is D)) return false;

			var item = this[(int)((D)value).Id];
			if (!(item is D)) return false;

			bool result = (D)item != null;
			return result;
		}

		/// <summary>
		/// Inserts an item to the <see cref="T:System.Collections.IList"/> at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index at which <paramref name="value"/> should be inserted. </param><param name="value">The object to insert into the <see cref="T:System.Collections.IList"/>. </param><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> is not a valid index in the <see cref="T:System.Collections.IList"/>. </exception><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IList"/> is read-only.-or- The <see cref="T:System.Collections.IList"/> has a fixed size. </exception><exception cref="T:System.NullReferenceException"><paramref name="value"/> is null reference in the <see cref="T:System.Collections.IList"/>.</exception><filterpriority>2</filterpriority>
		public void Insert( int index, object value )
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.IList"/>.
		/// </summary>
		/// <param name="value">The object to remove from the <see cref="T:System.Collections.IList"/>. </param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IList"/> is read-only.-or- The <see cref="T:System.Collections.IList"/> has a fixed size. </exception><filterpriority>2</filterpriority>
		public void Remove( object value )
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="T:System.Collections.IList"/> has a fixed size.
		/// </summary>
		/// <returns>
		/// true if the <see cref="T:System.Collections.IList"/> has a fixed size; otherwise, false.
		/// </returns>
		public bool IsFixedSize
		{
			get
			{
				return true;
			}
			private set
			{
				throw new NotImplementedException("IsFixedSize");
			}
		}

		#endregion
  }
}
